package com.megagoodsoftware.smugmug;
 
import java.io.*;
import java.net.*;
import java.util.*;

import org.apache.http.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.entity.*;
import org.apache.http.impl.client.*;
import org.apache.http.message.*;
import org.json.*;

import android.util.*;

import com.megagoodsoftware.smugmug.CountingHttpEntity.*;
import com.megagoodsoftware.smugmug.exceptions.*;
 
public class SmugMugClient {
	private final static String API_KEY = "ggmky75M7OFLskaXu6S6wu1SD6degX1Q";
	public final static String API_VERSION = "1.2.2";
	public static final String METHOD_CATEGORIES_GET = "smugmug.categories.get";
	public static final String METHOD_LOGOUT = "smugmug.logout";
	public static final String METHOD_IMAGES_GET = "smugmug.images.get";
	public static final String METHOD_IMAGES_UPLOAD = "smugmug.images.upload";
	public static final String METHOD_ALBUMS_GET = "smugmug.albums.get";
	public static final String METHOD_LOGIN_WITH_HASH = "smugmug.login.withHash";
	public static final String METHOD_LOGIN_WITH_PASSWORD = "smugmug.login.withPassword";
	private final static String SMUGMUG_URL = "https://secure.smugmug.com/services/api/json/" + API_VERSION;

	private HttpClient client;
 	private HttpPut uploadHttpPut;

 	public SmugMugClient() {
        client = new DefaultHttpClient();
 	}
	public void setClient(HttpClient client) {
		this.client = client;
	}
	protected HttpClient getClient() {
		return this.client;
	}

	private static String buildMethodUrl(String method) {
		return SMUGMUG_URL + "/?method=" + method + "&APIKey=" + API_KEY;
	}
	private static String buildMethodUrl(String method, String session) {
		return buildMethodUrl(method) + "&SessionId=" + session;
	}
	protected static void setUserAgent(AbstractHttpMessage message) {
		message.setHeader("User-Agent", Constants.USER_AGENT);
	}

    public static String convertStreamToString(InputStream is) {
        /*
         * To convert the InputStream to String we use the BufferedReader.readLine()
         * method. We iterate until the BufferedReader return null which means
         * there's no more data to read. Each line will appended to a StringBuilder
         * and returned as String.
         */
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
 
        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
			Log.e(Constants.TAG, "IOException while parsing stream", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
            	Log.e(Constants.TAG, "IOException while closing stream", e);
            }
        }
        return sb.toString();
    }
    
	private static void checkStatusOk(JSONObject json) throws JSONException, SmugMugFailedResponse {
		if ("ok".equals(json.getString("stat")))
			return;
		throw SmugMugFailedResponseFactory.createSmugMugFailedResponse(json
				.getInt("code"), json.getString("message"));

	}
	
	private void setAccountFromJSON(JSONObject json, SmugMugAccount acct) throws JSONException {
		JSONObject jsonLogin = json.getJSONObject("Login");
		acct.setSession(jsonLogin.getJSONObject("Session").getString("id"));
		acct.setType(jsonLogin.getString("AccountType"));
		acct.setFilesizeLimit(jsonLogin.getInt("FileSizeLimit"));
		acct.setNickName(jsonLogin.getJSONObject("User").getString("NickName"));
		acct.setDisplayName(jsonLogin.getJSONObject("User").getString(
				"DisplayName"));
	}

    public SmugMugAccount login(int userId, String hashPassword) throws SmugMugInternalError, SmugMugBadAuthentication
    {
		JSONObject json;
		try {
			json = connect(buildMethodUrl(METHOD_LOGIN_WITH_HASH) + "&PasswordHash=" + hashPassword + "&UserID=" + userId);
			SmugMugAccount acct = new SmugMugAccount();
			acct.setUserId(userId);
			acct.setPassword(hashPassword);
			setAccountFromJSON(json, acct);
    		return acct;
		} catch (JSONException e) {
			throw new SmugMugInternalError("Unable to parse JSON string");
		} catch (SmugMugBadAuthentication e) {
			Log.w(Constants.TAG, "Invalid Login", e);
			throw e;
		} catch (SmugMugInternalError e) {
			Log.w(Constants.TAG, "Couldn't Connect", e);
			throw e;
		} catch (Exception e) {
			Log.e(Constants.TAG, "uncaught exception: " + e.getLocalizedMessage(), e);
			throw new SmugMugInternalError("uncaught exception");
		}
    }
    
    public HashMap<String, SmugMugAlbum> getAllAlbums(String session) throws SmugMugInternalError, SmugMugFailedResponse {
    	HashMap<String, SmugMugAlbum> albums = new HashMap<String, SmugMugAlbum>();
    	JSONObject json =  connect(buildMethodUrl(METHOD_ALBUMS_GET, session));
    	JSONArray jsonArray;
		try {
			jsonArray = json.getJSONArray("Albums");
			for (int index = 0; index < jsonArray.length(); index++)
			{
				JSONObject jsonAlbum = jsonArray.getJSONObject(index);
	    		int albumId = jsonAlbum.getInt("id");
	    		String albumKey = jsonAlbum.getString("Key");
	    		String albumName = jsonAlbum.getString("Title");
	    		JSONObject jsonCategory= jsonAlbum.getJSONObject("Category");
	    		SmugMugAlbum album = new SmugMugAlbum(albumId, albumKey, albumName);
	    		album.setCategory(jsonCategory.getString("Name"));
	    		albums.put(Integer.toString(albumId), album);
			}
		} catch (JSONException e) {
			Log.e(Constants.TAG, "unable to parse JSON object", e);
			throw new SmugMugInternalError("unable to parse JSON");
		}
    	return albums;
    }

    private String connectString(String url) throws ClientProtocolException, IOException 
    {
    	String result = null;
		Log.d(Constants.TAG, url);
		// Prepare a request object
		HttpGet get = new HttpGet(url);
		setUserAgent(get);

		// Execute the request
		HttpResponse response = client.execute(get);
		// Examine the response status
		Log.d(Constants.TAG, response.getStatusLine().toString());

		// Get hold of the response entity
		HttpEntity entity = response.getEntity();
		// If the response does not enclose an entity, there is no need
		// to worry about connection release
		if (entity != null) {
			InputStream instream = null;
			try {
				instream = entity.getContent();
				result = convertStreamToString(instream);
				Log.d(Constants.TAG, result);
			} finally {
				// Closing the input stream will trigger connection release
				try {
					instream.close();
				} catch (Exception e) {
					Log.e(Constants.TAG, "unable to close stream", e);
				}
			}
		}
		return result;
    }
    protected JSONObject connect(String url) throws SmugMugInternalError, SmugMugFailedResponse
    {
    	try {
			JSONObject json = new JSONObject(connectString(url));
			checkStatusOk(json);
			
			return json;
		} catch (JSONException e) {
			Log.e(Constants.TAG, "Error while parsing JSON", e);
			throw new SmugMugInternalError(e.getLocalizedMessage());
		} catch (ClientProtocolException e) {
			Log.e(Constants.TAG, "Invalid protocol", e);
			throw new SmugMugInternalError(e.getLocalizedMessage());
		} catch (IOException e) {
			Log.e(Constants.TAG, "IOException occured", e);
			throw new SmugMugInternalError(e.getLocalizedMessage());
		} 
    }
    public SmugMugImage upload(ProgressListener listener, InputStream istream, String md5, String sessionId, SmugMugAlbum album, String name) throws SmugMugConnectionError, SmugMugFailedResponse, SmugMugInternalError {
    	try {
			uploadHttpPut = new HttpPut("http://upload.smugmug.com/" + URLEncoder.encode(name) + "?method=smugmug.images.upload");
			setUserAgent(uploadHttpPut);
			uploadHttpPut.setHeader("Content-MD5", md5);
			uploadHttpPut.setHeader("X-Smug-SessionID", sessionId);
			uploadHttpPut.setHeader("X-Smug-Version", API_VERSION);
			uploadHttpPut.setHeader("X-Smug-ResponseType", "JSON");
			uploadHttpPut.setHeader("X-Smug-AlbumID", Integer.toString(album.getId()));
			uploadHttpPut.setHeader("X-Smug-FileName", name);
			AbstractHttpEntity entity = new InputStreamEntity(istream, istream.available());
			if (listener == null)
				uploadHttpPut.setEntity(entity);
			else
				uploadHttpPut.setEntity(new CountingHttpEntity(entity, listener));
			HttpResponse response = client.execute(uploadHttpPut);
			Log.i(Constants.TAG, "Upload response status: " + response.getStatusLine().getReasonPhrase());
			String result = convertStreamToString(response.getEntity().getContent());
			Log.d(Constants.TAG, result);
			JSONObject j = new JSONObject(result);
			checkStatusOk(j);
			return new SmugMugImage(j.getJSONObject("Image").getInt("id"), j.getJSONObject("Image").getString("Key"), name);
    	} catch (IOException e) {
			Log.e(Constants.TAG, "IOException occured", e);
    		throw new SmugMugConnectionError();
    	} catch (JSONException e) {
    		Log.e(Constants.TAG, "error while parsing JSON object", e);
    		throw new SmugMugInternalError("error while parsing JSON object");
    	}
	}
	protected HttpPut getHttpPut() {
		return uploadHttpPut;
	}
	public boolean logout(String sessionId) {
    	try {
			connect(buildMethodUrl(METHOD_LOGOUT, sessionId));
			return true;
		} catch (SmugMugInternalError e) {
			Log.e(Constants.TAG, "Error occured while logging out", e);
		} catch (SmugMugInvalidSession e) {
			Log.i(Constants.TAG, "invalid session during logout (session expired?)");
		} catch (SmugMugFailedResponse e) {
			Log.e(Constants.TAG, "Bad response while logging out: " + e.getLocalizedMessage(), e);
		}
		return false;
	}
	public HashMap<String, SmugMugCategory> getAllCategories(String sessionId) throws SmugMugFailedResponse, SmugMugInternalError {
		try {
			JSONObject json = connect(buildMethodUrl(METHOD_CATEGORIES_GET,
					sessionId));
			JSONArray jsonArray = json.getJSONArray("Categories");
			HashMap<String, SmugMugCategory> categories = new HashMap<String, SmugMugCategory>();
			for (int index = 0; index < jsonArray.length(); index++) {
				JSONObject jsonCat = jsonArray.getJSONObject(index);
				int catId = jsonCat.getInt("id");
				String catName = jsonCat.getString("Name");
				SmugMugCategory category = new SmugMugCategory(catId, catName);
				categories.put(Integer.toString(catId), category);
			}
			return categories;
		} catch (SmugMugFailedResponse e) {
			Log.w(Constants.TAG, "failed response received: "
					+ e.getLocalizedMessage());
			throw e;
		} catch (JSONException e) {
			Log.e(Constants.TAG, "unable to parse JSON object", e);
			throw new SmugMugInternalError("unable to parse JSON");
		} catch (Exception e) {
			Log.e(Constants.TAG, "uncaught exception: "
					+ e.getLocalizedMessage(), e);
			throw new SmugMugInternalError("uncaught exception");
		}
	}
	public SmugMugAccount login(String email, String password) throws SmugMugInternalError, SmugMugBadAuthentication {
		JSONObject json;
		try {
			json = connect(buildMethodUrl(METHOD_LOGIN_WITH_PASSWORD) + "&EmailAddress=" + email + "&Password="	+ password);
			SmugMugAccount acct = new SmugMugAccount();
			JSONObject jsonLogin = json.getJSONObject("Login");
			acct.setUserId(jsonLogin.getJSONObject("User").getInt("id"));
			acct.setPassword(jsonLogin.getString("PasswordHash"));
			setAccountFromJSON(json, acct);
    		return acct;
		} catch (JSONException e) {
			throw new SmugMugInternalError("Unable to parse JSON string");
		} catch (SmugMugBadAuthentication e) {
			Log.w(Constants.TAG, "Invalid Login", e);
			throw e;
		} catch (SmugMugFailedResponse e) {
			String msg = "Failed Response, code: " + e.getCode() + " message: " + e.getMessage();
			Log.e(Constants.TAG, msg);
			throw new SmugMugInternalError(msg);
		} catch (Exception e) {
			Log.e(Constants.TAG, "uncaught exception", e);
			throw new SmugMugInternalError("uncaught exception");
		}
	}
	public void updateImages(String sessionId, SmugMugAlbum album) throws SmugMugFailedResponse, SmugMugInternalError {
		updateImages_new(sessionId, album);
	}
	
	public void updateImages_new(String sessionId, SmugMugAlbum album) throws SmugMugFailedResponse, SmugMugInternalError {
		try {
			JSONObject json = connect(buildMethodUrl(METHOD_IMAGES_GET,
					sessionId) + "&AlbumID=" + album.getId() + "&AlbumKey=" + album.getKey() + "&Heavy=true");
			JSONObject jsonAlbum = json.getJSONObject("Album");
			JSONArray jsonArray = jsonAlbum.getJSONArray("Images");
			album.clearImages();
			for (int index = 0; index < jsonArray.length(); index++) {
				JSONObject jsonImage = jsonArray.getJSONObject(index);
				int id = jsonImage.getInt("id");
				String key = jsonImage.getString("Key");
				String name = jsonImage.getString("FileName");
				SmugMugImage image = new SmugMugImage(id, key, name);
				image.setCaption(jsonImage.getString("Caption"));
				image.setTinyUrl(jsonImage.getString("TinyURL"));
				image.setThumbUrl(jsonImage.getString("ThumbURL"));
				image.setSmallUrl(jsonImage.getString("SmallURL"));
				image.setMediumUrl(jsonImage.getString("MediumURL"));
				album.addImage(image);
			}
		} catch (SmugMugFailedResponse e) {
			Log.w(Constants.TAG, "failed response received: "
					+ e.getLocalizedMessage());
			throw e;
		} catch (JSONException e) {
			Log.e(Constants.TAG, "unable to parse JSON object", e);
			throw new SmugMugInternalError("unable to parse JSON");
		} catch (Exception e) {
			Log.e(Constants.TAG, "uncaught exception: "
					+ e.getLocalizedMessage(), e);
			throw new SmugMugInternalError("uncaught exception");
		}
	}

	public void updateImages_old(String sessionId, SmugMugAlbum album) throws SmugMugFailedResponse, SmugMugInternalError {
		try {
			JSONObject json = connect(buildMethodUrl(METHOD_IMAGES_GET,
					sessionId) + "&AlbumID=" + album.getId() + "&AlbumKey=" + album.getKey() + "&Heavy=true");
			JSONArray jsonArray = json.getJSONArray("Images");
			album.clearImages();
			for (int index = 0; index < jsonArray.length(); index++) {
				JSONObject jsonImage = jsonArray.getJSONObject(index);
				int id = jsonImage.getInt("id");
				String key = jsonImage.getString("Key");
				String name = jsonImage.getString("FileName");
				SmugMugImage image = new SmugMugImage(id, key, name);
				image.setCaption(jsonImage.getString("Caption"));
				image.setTinyUrl(jsonImage.getString("TinyURL"));
				image.setThumbUrl(jsonImage.getString("ThumbURL"));
				image.setSmallUrl(jsonImage.getString("SmallURL"));
				image.setMediumUrl(jsonImage.getString("MediumURL"));
				album.addImage(image);
			}
		} catch (SmugMugFailedResponse e) {
			Log.w(Constants.TAG, "failed response received: "
					+ e.getLocalizedMessage());
			throw e;
		} catch (JSONException e) {
			Log.e(Constants.TAG, "unable to parse JSON object", e);
			throw new SmugMugInternalError("unable to parse JSON");
		} catch (Exception e) {
			Log.e(Constants.TAG, "uncaught exception: "
					+ e.getLocalizedMessage(), e);
			throw new SmugMugInternalError("uncaught exception");
		}
	}

}
